using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;

namespace TelnetExpect
{
    /// <summary>
    /// Allows the peer to enable <see cref="F:TelnetExpect.TelnetOption.Status"/>
    /// by negotiation, and when the peer offers its list of option status it
    /// logs the contents of the list.
    /// </summary>
    /// -
    /// <remarks>
    /// <para>&#x201C;Once the two hosts have exchanged a WILL and a DO, the sender of the
    /// WILL STATUS is free to transmit status information, spontaneously or
    /// in response to a request from the sender of the DO.&#x201D; RFC0859
    /// </para>
    /// <para>To request that the peer sends a status report call method 
    /// <see cref="M:RequestStatus"/>.
    /// </para>
    /// <para>If you want to handle the status report override method
    /// <see cref="M:OnReceivedStatus"/>.
    /// </para>
    /// </remarks>
    public class StatusReceiveTelnetHandler : TelnetOptionHandler
    {
        /// <summary>
        /// Initializes an instance of class <see cref="T:StatusReceiveTelnetHandler"/>.
        /// </summary>
        public StatusReceiveTelnetHandler()
        {
        }

        /// <exclude/>
        protected internal override void Init()
        {
            base.Init();
            Telnet.SetRemoteMode(TelnetOption.Status, true);
        }

        //----
        /// <summary>
        /// Gets whether the remote device
        /// has negotiated support for the Status option.
        /// </summary>
        public bool IsSupported { get { return Telnet.GetRemoteState(TelnetOption.Status); } }

        /// <summary>
        /// Sends a request for the peer to send a Status Report.
        /// </summary>
        /// -
        /// <exception cref="T:System.InvalidOperationException">The remote device
        /// has not negotiated support for the Status option.  See <see cref="P:IsSupported"/>
        /// </exception>
        public void RequestStatus()
        {
            if (!IsSupported)
                throw new InvalidOperationException("Remove device does not support Status.");
            byte[] reqSubNego = { (byte)TelnetOption.Status, (byte)StatusCmd.SEND };
            Telnet.SendTelnetSubNegotiation(reqSubNego);
        }

        //----
        /// <exclude/>
        protected sealed override void HandleSubNegotiation(byte[] data)
        {
            if (data.Length >= 2) {
                TelnetOption id = (TelnetOption)data[0];
                StatusCmd cmd = (StatusCmd)data[1];
                if (id == TelnetOption.Status) {
                    if (cmd == StatusCmd.IS) {
                        int offset = 2;
                        List<TelnetOption> enabledDos = new List<TelnetOption>();
                        List<TelnetOption> enabledWills = new List<TelnetOption>();
                        while (data.Length - offset >= 2) {
                            TelnetBaseStream.Command willOrDo = (TelnetBaseStream.Command)data[0 + offset];
                            TelnetOption option = (TelnetOption)data[1 + offset];
                            if (willOrDo == TelnetBaseStream.Command.Will) {
                                enabledWills.Add(option);
                            } else if (willOrDo == TelnetBaseStream.Command.Do) {
                                enabledDos.Add(option);
                            } else {
                                TraceWriteLine("Unexpected Status first pair byte: " + willOrDo + "; second is: " + option);
                            }
                            offset += 2;
                        }//while
                        Debug.Assert(data.Length - offset == 0, "pairs!");
                        TraceWriteLine("Received Status: my/remote: [ {0} ]; yours/local: [ {1} ].",
                            TelnetBaseStream.Join(enabledWills), TelnetBaseStream.Join(enabledDos));
                        enabledWills.Sort();
                        enabledDos.Sort();
                        OnReceivedStatus(enabledWills.ToArray(), enabledDos.ToArray());
                        // TODO Mark sub-negotiation as used!
                        //  -- parent assume so only if we call AddResponseSubNegotiation!
                        return;
                    } else if (cmd == StatusCmd.SEND) {
                        // Not meant for us, meant for the reverse operation handler class.
                    }
                    TraceWriteLine("Discarding unparsable Status report.");
                }//Status
            }//len
        }

        /// <summary>
        /// When overridden in a derived class, receives the content of received
        /// status reports.
        /// </summary>
        /// <param name="myRemoteOptions">The list of the options that the peer
        /// believes is enabled on his side.
        /// </param>
        /// <param name="yourLocalOptions">The list of the options that the peer
        /// believes is enabled on our side.
        /// </param>
        protected virtual void OnReceivedStatus(TelnetOption[] myRemoteOptions, TelnetOption[] yourLocalOptions)
        {
        }

        //----
        enum StatusCmd
        {
            IS = 0,
            SEND = 1,
        }

    }
}
